/*
 * Copyright (c) 2008, University of Aarhus.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - Neither the name of the Univeristy of Aarhus nor the names
 *   of its contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author: Morten Tranberg Hansen <mth@cs.au.dk>
 * @date:   May 8 2008	
 * 
 */
 
#include <Timer.h>
#include "sensobyg.h"

module SensoBygP {

	uses {
		/*** Radio Control ***/
		interface SplitControl as RadioControl;

		/*** Data Collection ***/
		interface StdControl as CollectionControl;
		interface Send as CollectionSend;
		interface Receive as CollectionReceive;
		interface RootControl;
		interface CollectionPacket as CollectionPacket;

		/*** Command Dissemination ***/
		interface StdControl as DisseminationControl;
		interface DisseminationValue<sensobyg_command_msg_t> as CommandValue;
		interface DisseminationUpdate<sensobyg_command_msg_t> as CommandUpdate;
   		interface Receive as CommandReceive;

#ifdef LOW_POWER_LISTENING
		interface LowPowerListening;
#endif

		/*** Serial Communication (only used by roots) ***/
		interface SplitControl as SerialControl;
		interface AMSend as SerialSend[am_id_t id];
		interface AMPacket as SerialAMPacket;
		interface Packet as SerialPacket;
	    interface Queue<message_t*> as SerialQueue;
	    interface Pool<message_t> as SerialMessagePool;

		/*** Misc ***/
		interface Boot;
		interface Leds;
		interface Timer<TMilli> as Timer;
		interface Read<uint16_t> as TemperatureSensor;
		interface LocalTime<TMilli> as LocalTime;
	}
}
implementation {
	message_t dataMsg;
	bool dataBusy = FALSE, serialBusy = FALSE;

	/***************** Boot and Starup****************/

	event void Boot.booted() {
#ifdef LOW_POWER_LISTENING
		call LowPowerListening.setLocalSleepInterval(DEFAULT_LPL_INTERVAL);
#endif
		call RadioControl.start();
	    dbg("SensoByg.debug", "%s Booted.\n", __FUNCTION__);
	}
  
	event void RadioControl.startDone(error_t error) {
    	if (error != SUCCESS)  {
		    dbgerror("SensoByg.error", "%s Radio could not be started.\n", __FUNCTION__);
		    call RadioControl.start();
	    } else {
			dbg("SensoByg.debug", "%s Radio started.\n", __FUNCTION__);
		    call CollectionControl.start();
		    call DisseminationControl.start();
			if (TOS_NODE_ID == 0) {
				call RootControl.setRoot();
				call SerialControl.start();
		    } else {
				call Timer.startPeriodic(DEFAULT_SAMPLE_INTERVAL);
		    }
		}	  
  	}

	event void RadioControl.stopDone(error_t err) {}

	event void SerialControl.startDone(error_t error) {
		if (error != SUCCESS)  {
		    dbgerror("SensoByg.error", "%s Serial could not be started.\n", __FUNCTION__);
		    call SerialControl.start();
		} else {
			dbg("SensoByg.debug", "%s Serial started.\n", __FUNCTION__);
		}
	}

	event void SerialControl.stopDone(error_t err) {}

	/***************** Serial Functions ****************/
	
  	task void serialSendTask() {
  		message_t* nextMsg;
		sensobyg_serial_data_msg_t* smsg;
  		am_id_t id;
  		am_addr_t addr;
  		uint8_t length;
  		
  		if(call SerialQueue.empty()) {
  			dbgerror("SensoByg.error", "%s Task posted but queue was empty.\n", __FUNCTION__);
  			return;
  		}
  			
  		if(serialBusy) {
	  		dbgerror("SensoByg.error", "%s Task posted while serial is busy.\n", __FUNCTION__);
  			return;
  		}
  		
  		nextMsg = call SerialQueue.head();
  		
  		smsg = (sensobyg_serial_data_msg_t*) call SerialPacket.getPayload(nextMsg, call SerialPacket.maxPayloadLength());
  		dbg("SensoByg.debug,test", "%s Sending message with from %hhu with timestamp %u and data %u to serial.\n", __FUNCTION__, smsg->origin, smsg->data.timestamp, smsg->data.data); 
  		
  		addr = call SerialAMPacket.source(nextMsg);
  		length = call SerialPacket.maxPayloadLength();
  		id = call SerialAMPacket.type(nextMsg);
  		
  		if(call SerialSend.send[id](addr, nextMsg, length) != SUCCESS) {
  			dbgerror("SensoByg.error", "%s Failed to send message to serial with id %hhu and length %hhu.\n", __FUNCTION__, id, length);
  		} else {
  			serialBusy = TRUE;
  		}
  	}

	
	event void SerialSend.sendDone[am_id_t id](message_t* msg, error_t error) {
		if(msg!=call SerialQueue.head()) {
			return;
		}
		if(error != SUCCESS) { dbgerror("SensoByg.error", "%s Failed sending message to serial.\n", __FUNCTION__); }
		if(call SerialMessagePool.size()==call SerialMessagePool.maxSize()) { dbgerror("SensoByg.error", "%s Pool is full but should not be.\n", __FUNCTION__); }
		if(call SerialQueue.empty()) { dbgerror("SensoByg.error", "%s Queue is empty but should not be.\n", __FUNCTION__); }

		dbg("SensoByg.debug", "%s Serial Message send.\n", __FUNCTION__); 

		call SerialMessagePool.put(msg);
		call SerialQueue.dequeue();
		serialBusy = FALSE;
		
		if(!call SerialQueue.empty()) {
			post serialSendTask();
		}
	}
  	
  	/***************** Data Sampling ****************/
  	
  	void sendDataMessage() {
#ifdef LOW_POWER_LISTENING
    	call LowPowerListening.setRxSleepInterval(&dataMsg, DEFAULT_LPL_INTERVAL);
#endif

	    if (call CollectionSend.send(&dataMsg, sizeof(sensobyg_data_msg_t)) != SUCCESS) {
		    dbgerror("SensoByg.error", "%s Failed sending.\n", __FUNCTION__); 
	    } else {
		    sensobyg_data_msg_t* msg = (sensobyg_data_msg_t*) call CollectionSend.getPayload(&dataMsg, sizeof(sensobyg_data_msg_t));
		    dataBusy = TRUE;
		    dbg("SensoByg.debug", "%s Message send from %hu\n", __FUNCTION__, TOS_NODE_ID, msg->timestamp, msg->data); 
	    }
	}
	
	event void Timer.fired() {
	    dbg("SensoByg.debug,test", "%s Timer Fired at %u.\n", __FUNCTION__, (call LocalTime.get())/1024); 
		call TemperatureSensor.read();
	}
	
	event void TemperatureSensor.readDone(error_t error, uint16_t value) {
		sensobyg_data_msg_t* msg;
		
		if(error!=SUCCESS) {
			dbgerror("SensoByg.error", "%s Error reading from temperature sensor.\n", __FUNCTION__); 
			return;
		}
		
		if (dataBusy) {
			dbgerror("SensoByg.error", "%s Send is busy.\n", __FUNCTION__); 
			return;
		}
		
		msg = (sensobyg_data_msg_t*) call CollectionSend.getPayload(&dataMsg, sizeof(sensobyg_data_msg_t));
	    msg->timestamp = (call LocalTime.get())/1024;
	    msg->data_type = DATA_TYPE_TEMPERATURE;
	    msg->data = value;
		   	
		sendDataMessage(); 
	}
  
	/***************** Collection Functions ****************/
  
	event void CollectionSend.sendDone(message_t* message, error_t err) {
		if (err == SUCCESS) {
			dbg("SensoByg.debug", "%s Send done.\n", __FUNCTION__);
		} else {
		    dbgerror("SensoByg.error", "%s Send failed.\n", __FUNCTION__);
	    }
	    dataBusy = FALSE;
	}
  
    /** 
  	  * Only called at root.
  	  *
  	  */
	event message_t* CollectionReceive.receive(message_t* message, void* payload, uint8_t len) {
		message_t* newMessage;
		sensobyg_serial_data_msg_t* serialMsg;
		
		if(call SerialMessagePool.empty()) {
			dbgerror("SensoByg.error", "%s Serial Queue is full so packet dropped.\n", __FUNCTION__);
			return message;
		}
	
		dbg("SensoByg.debug", "%s Message Queued.\n", __FUNCTION__); 
		
		// Construct new AM serial message from received collection message
		newMessage = call SerialMessagePool.get();
		call SerialPacket.clear(newMessage);
		serialMsg = (sensobyg_serial_data_msg_t*) call SerialPacket.getPayload(newMessage, sizeof(sensobyg_serial_data_msg_t));
		serialMsg->origin = call CollectionPacket.getOrigin(message);
		serialMsg->seqno = call CollectionPacket.getSequenceNumber(message);
		memcpy(&serialMsg->data, payload, sizeof(sensobyg_data_msg_t));
		call SerialAMPacket.setType(newMessage, AM_SENSOBYG_SERIAL_DATA_MSG);
				
		if(call SerialQueue.enqueue(newMessage) != SUCCESS) {
			dbgerror("SensoByg.error", "%s Mismatch betweeen Queue and Pool size.\n", __FUNCTION__);
		}
		
		post serialSendTask();

		return message;
	}

	/***************** Command Functions ****************/
  
  	event void CommandValue.changed() {
  	    if(!call RootControl.isRoot()) {
 	 	    const sensobyg_command_msg_t* conf = call CommandValue.get();
  		    switch(conf->type) {
  		    case COMMAND_TYPE_TEMPERATURE:
  		    	dbg("SensoByg.debug,test", "%s Temperature sample interval changed to %u.\n", __FUNCTION__, conf->interval);
  	    		call Timer.startPeriodicAt(call Timer.gett0(), conf->interval);
  	    		break;
  	    	}
  	    }
  	}
  	
  	event message_t* CommandReceive.receive(message_t* message, void* payload, uint8_t len) {
  		sensobyg_command_msg_t* conf = (sensobyg_command_msg_t*) call SerialPacket.getPayload(message, sizeof(sensobyg_command_msg_t));
  		dbg("SensoByg.debug,test", "%s Configuration message received with sample interval %u.\n", __FUNCTION__, conf->interval);
  		call CommandUpdate.change(conf);
  		return message;
  	}
  
}
